/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: Peripheral Component Interconnect bus
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2022-08-13     JasonHu           Init
 */

#ifndef __X86_PCI_H__
#define __X86_PCI_H__

#include <nxos_mini.h>
#include <base/list.h>

#define PCI_CONFIG_ADDR    0xCF8    /* PCI configuration space address port */
#define PCI_CONFIG_DATA    0xCFC    /* PCI configuration space data port */

#define PCI_DEVICE_VENDER               0x00
#define PCI_STATUS_COMMAND              0x04
#define PCI_CLASS_CODE_REVISION_ID      0x08
#define PCI_BIST_HEADER_TYPE            0x0C
#define PCI_BASS_ADDRESS0               0x10
#define PCI_BASS_ADDRESS1               0x14
#define PCI_BASS_ADDRESS2               0x18
#define PCI_BASS_ADDRESS3               0x1C
#define PCI_BASS_ADDRESS4               0x20
#define PCI_BASS_ADDRESS5               0x24
#define PCI_CARD_BUS_POINTER            0x28
#define PCI_SUBSYSTEM_ID                0x2C
#define PCI_EXPANSION_ROM_BASE_ADDR     0x30
#define PCI_CAPABILITY_LIST             0x34
#define PCI_RESERVED                    0x38
#define PCI_IRQ_PIN_IRQ_LINE            0x3C

#define  PCI_COMMAND_IO                 0x1     /* Enable response in I/O space */
#define  PCI_COMMAND_MEMORY             0x2     /* Enable response in Memory space */
#define  PCI_COMMAND_MASTER             0x4     /* Enable bus mastering */
#define  PCI_COMMAND_SPECIAL            0x8     /* Enable response to special cycles */
#define  PCI_COMMAND_INVALIDATE         0x10    /* Use memory write and invalidate */
#define  PCI_COMMAND_VGA_PALETTE        0x20    /* Enable palette snooping */
#define  PCI_COMMAND_PARITY             0x40    /* Enable parity checking */
#define  PCI_COMMAND_WAIT               0x80    /* Enable address/data stepping */
#define  PCI_COMMAND_SERR               0x100   /* Enable SERR */
#define  PCI_COMMAND_FAST_BACK          0x200   /* Enable back-to-back writes */
#define  PCI_COMMAND_INTX_DISABLE       0x400   /* INTx Emulation Disable */

#define PCI_BASE_ADDR_MEM_MASK           (~0x0FUL)
#define PCI_BASE_ADDR_IO_MASK            (~0x03UL)

#define PCI_BAR_TYPE_INVALID     0
#define PCI_BAR_TYPE_MEM         1
#define PCI_BAR_TYPE_IO         2

#define PCI_MAX_BAR_NR 6        /* Each device has up to 6 address information */
#define PCI_MAX_BUS_NR 256      /* PCI has a total of 256 buses */
#define PCI_MAX_DEV_NR 32       /* There are a total of 32 devices on each PCI bus */
#define PCI_MAX_FUN_NR 8        /* PCI devices have a total of 8 function numbers */

#ifndef PCI_ANY_ID
#define PCI_ANY_ID (~0)
#endif

typedef struct NX_PciDeviceId
{
    NX_U32 vendor, device;        /* vendor and device id or PCI_ANY_ID */
    NX_U32 subvendor, subdevice;  /* subsystem's id or PCI_ANY_ID */
    NX_U32 class_value, class_mask;
} NX_PciDeviceId;

typedef struct NX_PciDeviceBar
{
    NX_U32 type;          /* Type of address bar (IO address/MEM address) */
    NX_U32 base_addr;
    NX_U32 length;        /* Length of address */
} NX_PciDeviceBar;

typedef struct NX_PciDevice
{
    NX_List list;          /* list for all pci device */
    NX_U8 bus;             /* bus number */
    NX_U8 dev;             /* device number */
    NX_U8 function;        /* Function number */

    NX_U16 vendor_id;      /* Configuration space:Vendor ID */
    NX_U16 device_id;      /* Configuration space:Device ID */
    NX_U16 command;        /* Configuration space:Command */
    NX_U16 status;         /* Configuration space:Status */

    NX_U32 class_code;     /* Configuration space:Class Code */
    NX_U8 revision_id;     /* Configuration space:Revision ID */
    NX_U8 multi_function;
    NX_U32 card_bus_pointer;
    NX_U16 subsystem_vendor_id;
    NX_U16 subsystem_device_id;
    NX_U32 expansion_rom_base_addr;
    NX_U32 capability_list;

    NX_U8 irq_line;        /*Configuration space:IRQ line*/
    NX_U8 irq_pin;         /*Configuration space:IRQ pin*/
    NX_U8 min_gnt;
    NX_U8 max_lat;
    NX_PciDeviceBar bars[PCI_MAX_BAR_NR];
} NX_PciDevice;

NX_U32 NX_PciDeviceGetIoAddr(NX_PciDevice *device);
NX_U32 NX_PciDeviceGetMemAddr(NX_PciDevice *device);
NX_U32 NX_PciDeviceGetMemLength(NX_PciDevice *device);
NX_U32 NX_PciDeviceGetIrqLine(NX_PciDevice *device);

void NX_PciEnableBusMastering(NX_PciDevice *device);

NX_PciDevice* NX_PciDeviceGet(NX_U32 vendor_id, NX_U32 device_id);
NX_PciDevice* NX_PciDeviceGetByClassCode(NX_U32 class_value, NX_U32 sub_class);

void NX_PciDeviceBarDump(NX_PciDeviceBar *bar);
void NX_PciDeviceDump(NX_PciDevice *device);

NX_U32 NX_PciDeviceRead(NX_PciDevice *device, NX_U32 reg);
void NX_PciDeviceWrite(NX_PciDevice *device, NX_U32 reg, NX_U32 value);

NX_U32 NX_PciReadConfig(NX_U32 bus, NX_U32 device, NX_U32 function, NX_U32 addr);
void NX_PciWriteConfig(NX_U32 bus, NX_U32 device, NX_U32 function, NX_U32 addr, NX_U32 val);

#endif  /* __X86_PCI_H__ */